#!/usr/bin/env python2
'''
Created on Jul 1, 2011
TODO:
1. Load channel list from M3U file                DONE
2. Detect launch type by extension or marker      DONE
3. Add sopcast support                            DONE
4. Full-screen checkbox                           DONE
5. Volume control?
6. Tree control?
7. embed console output
8. asx parsing
9. open/save/edt playlist                         DONE
10. Edit Source code                              DONE
11. handle screensaver!!! xorg, win32(ctypes)     DONE
12. on top
13. add 'open with system default engine' by extension
14. add 'open with default browser'               DONE
15. Recent playlists submenu?
16. save settings in #EXT..                          DONE
17, Enable custom command line via #EXT.. use human language
     combine both above to context EDIT menu on item context
18. Add search field  DONE
19. Add mplayer slave mode

20. ADD WORKAROUND for missing `xterm` executable

@author: xh
'''

#hack for python3
#import sip
#sip.setapi('QVariant', 1)

#from PyQt4.QtCore import * #@UnusedWildImport
#from PyQt4.QtGui import * #@UnusedWildImport
#from PyQt4.Qt import Qt
from PyQt4.QtCore import  Qt
from PyQt4.QtGui import QMessageBox
from PyQt4.QtGui import QIcon
from PyQt4.QtGui import QPixmap
from PyQt4.QtGui import QListWidget
from PyQt4.QtGui import QListWidgetItem
from PyQt4.QtGui import QFileDialog
from PyQt4.QtGui import QCheckBox
from PyQt4.QtGui import QGridLayout
from PyQt4.QtGui import QPushButton
from PyQt4.QtGui import QAction
from PyQt4.QtGui import QWidget
from PyQt4.QtGui import QLabel
from PyQt4.QtGui import QSizePolicy
from PyQt4.QtGui import QRadioButton
from PyQt4.QtGui import QGroupBox
from PyQt4.QtGui import QHBoxLayout
from PyQt4.QtGui import QApplication
from PyQt4.QtGui import QColor
from PyQt4.QtGui import QLineEdit
from PyQt4.QtGui import QToolButton
from PyQt4.QtCore import QSize
from PyQt4.QtCore import QSettings
from PyQt4.QtCore import QVariant
from PyQt4.QtCore import QPoint
from PyQt4.Qt import SIGNAL

#from PyQt4.QtCore.Qt import Key_Escape
#from PyQt4.QtCore.Qt import ActionsContextMenu
#from PyQt4.QtCore.Qt import ArrowCursor
#from PyQt4.QtCore.Qt import ItemIsSelectable
#from PyQt4.QtCore.Qt import ItemIsEnabled

import codecs
import os
import sys
import webbrowser
import traceback
import socket
import shutil
import time
import subprocess


MATE_BINARY = '/usr/local/bin/mate'

VERSION = '3.5.14'
APPNAME = 'ChanChanTV'
AUTHOR = 'xhuman'
EMAIL = 'ZG9ncGl6emFAZ21haWwuY29t\n'
WEB = "http://chanchantv.googlecode.com"
DEFAULT_CHANNELS_FILENAME = 'mychannels.m3u'
MPLAYER = 'mplayer'
VLC = 'vlc'
MPLAYER_PATH_WIN32 = r'd:\pf\mplayer\mplayer.exe'
VLC_PATH_WIN32 = os.path.join(os.getenv('PROGRAMFILES', r'c:\program files'), 'VideoLAN', 'VLC','vlc.exe')
VLC_PATH_WIN32_CUSTOM = r'd:\Program Files (x86)\VideoLAN\VLC\vlc.exe'
VLC_PATH_OSX='/Applications/VLC.app/Contents/MacOS/VLC'
SOPCAST_SERVER_WAIT_SECS = 8
CACHE_SIZE_DEFAULT = '30000'
CACHE_SIZE_MIN = '2'
IS_FULLSCREEN_DEFAULT = False
IS_ON_TOP_DEFAULT = False
GOOD_VALUES = ('true', 'yes', '1')
SOPCAST_LISTEN_PORT = '37557'
SOPCAST_SERVER_PORT = '38558'
SOPCAST_SERVER_URL = 'http://127.0.0.1:%s' % SOPCAST_SERVER_PORT
VALID_META_KEYS=('player','ontop','cache','playlist','fullscreen', 'exec', 'shell', 'terminal')
VLC_SOCKET_FNAME = 'vlc.socket'

SCREENSAVER_SUSPEND = 'gnome-screensaver-command -i -n mplayer -r video'
sopcast_binary = 'sopcast'

print 'Python Version=', sys.version, '\n', APPNAME, VERSION
platform = sys.platform

is_win32 = platform == "win32"
is_osx = platform == 'darwin'
is_linux = platform == 'linux2'


if is_win32:
    try:
        import ctypes
    except:
        print 'ctypes module not found! cannot manage screensaver'
        ctypes = None

if is_osx:
    sopcast_binary = '/Applications/SopCast.app/Contents/Resources/binaries/m32/sp-sc-auth'

def get_win32_editor():
    pf = os.path.join(os.getenv('PROGRAMFILES', r'c:\program files'))
    
    if os.path.exists(os.path.join(pf, 'scite', 'scite.exe')):
        return os.path.join(pf, 'scite', 'scite.exe')
    my_editor = r'd:\program files\scite\scite.exe'
    
    if os.path.exists(my_editor):
        return my_editor

    return 'notepad'

def get_osx_editor():
    if os.path.exists(MATE_BINARY):
        editor = MATE_BINARY
    else:
        editor = 'open -a TextEdit'
    return editor

def run_command_in_new_terminal(*args):
    if is_osx:
        pass
    else:
        args = ['xterm', '-geometry', '45x8-20+400', '-e'] + args

    return subprocess.Popen(args)

EDITOR = is_osx and get_osx_editor() \
    or is_win32 and get_win32_editor() \
    or os.path.exists('/usr/bin/scite') and 'scite' \
    or os.path.exists('/usr/bin/gvim') and 'gvim' \
    or os.path.exists('/usr/bin/leafpad') and 'leafpad' \
    or os.path.exists('/usr/bin/mousepad') and 'mousepad' \
    or os.getenv('EDITOR', 'vim')

# window_id = None

imgdata_png_main = ('''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''')


imgdata_png_main_old = ('''iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz
AAALEgAACxIB0t1+/AAAABh0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzT7MfTgAABBF0RVh0
WE1MOmNvbS5hZG9iZS54bXAAPD94cGFja2V0IGJlZ2luPSIgICAiIGlkPSJXNU0wTXBDZWhpSHpy
ZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0
az0iQWRvYmUgWE1QIENvcmUgNC4xLWMwMzQgNDYuMjcyOTc2LCBTYXQgSmFuIDI3IDIwMDcgMjI6
Mzc6MzcgICAgICAgICI+CiAgIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcv
MTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgICAgIDxyZGY6RGVzY3JpcHRpb24gcmRmOmFi
b3V0PSIiCiAgICAgICAgICAgIHhtbG5zOnhhcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4w
LyI+CiAgICAgICAgIDx4YXA6Q3JlYXRvclRvb2w+QWRvYmUgRmlyZXdvcmtzIENTMzwveGFwOkNy
ZWF0b3JUb29sPgogICAgICAgICA8eGFwOkNyZWF0ZURhdGU+MjAxMC0xMS0xOFQyMTozMTozOFo8
L3hhcDpDcmVhdGVEYXRlPgogICAgICAgICA8eGFwOk1vZGlmeURhdGU+MjAxMC0xMS0xOFQyMjow
OTo0MFo8L3hhcDpNb2RpZnlEYXRlPgogICAgICA8L3JkZjpEZXNjcmlwdGlvbj4KICAgICAgPHJk
ZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6ZGM9Imh0dHA6Ly9w
dXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIj4KICAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9wbmc8
L2RjOmZvcm1hdD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94Onht
cG1ldGE+CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAg18ZVGgAAAZRJREFUaIHtmTFLw0AYhp9UqAiCu5k6BbIKgpOT0En/nj9B
EJycFKRT/0ShILR0KiiUrnG4SwnxLsklJN8F7oEjpDnue95ckoMryBEDb0AGfAAzQRdnYmCFks/b
QlLIBZN8Bhz7KDZDTW+Gmu6443g2+Qx47zi2kUWpyIr2Iark1x3GreRoKNYmhIg8qGk1FXUJISaf
F19bijcJISpflGgTwgv5ooxLCK/ki1JNQngpn1MX4hYh+Rh4ATaW4j61jXY93YwE2Hsg5tr2QHoG
PAM31RPkJRfAdQT8AFfCMm05RKjpGDWm52sJpAPVT3U908v6CEz10faBMf44lHxOanB4KPW5N/Sx
BpCg7HBZuj419MkmQxo6cldzfsLXGfgG5vraXJ//c7V9haK+bQ20unE+P0KNCAF6ZAs8Aef6uLV1
9PUlDuuANGEdGJqwDoySEECaEECaEECaEEAaWwCJbZXW2Da2ku5ejUgwb2w1aqPfWpwAv9ISHThM
gC9piw58gnqBRvsHR54kBl6BnQdidW2nXWOAPxiZxPLFR8k8AAAAAElFTkSuQmCC'''
)

imgdata_png_clear = (
'''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'''
)

SOPCAST_INSTALL_HOWTO = '''
Ubuntu/Debian:
1. echo "deb http://ppa.launchpad.net/jason-scheunemann/ppa/ubuntu `lsb_release -cs` main" | sudo tee -a /etc/apt/sources.list && sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys CD30EE56
2. sudo apt-get update && sudo apt-get install sopcast-player
Archlinux:
    sudo pacman -S sopcast
'''

def show_gui_error(e, error_text=''):
    full_error = error_text + '\n\n' + str(e) + '\n\n' + traceback.format_exc()
    print 'DBG:', full_error
    QMessageBox.warning(None, APPNAME, full_error)

def get_default_channels_path():
    if is_win32:
        folder =  get_my_documents_dir()
    else:
        folder = os.path.expanduser("~")
    return os.path.join(folder, DEFAULT_CHANNELS_FILENAME)

def get_my_documents_dir():
    if not ctypes:
        return ""
    dll = ctypes.windll.shell32
    buf = ctypes.create_string_buffer(300)
    dll.SHGetSpecialFolderPathA(None, buf, 0x0005, False)
    return buf.value

def copy_default_playlist():
    QMessageBox.warning(None, APPNAME,"This is the first time run!\nA default channels files will be created in your Documents folder.")
    my_playlist = get_default_channels_path()
    # if is_win32:
        # my_playlist = os.path.join(get_my_documents_dir(), DEFAULT_CHANNELS_FILENAME)
        # print 'Windows detected, playlist name: ', my_playlist

    # if no default playlist then copy playlist file from the program folder
    if not os.path.exists(my_playlist):
        exec_dir = os.path.dirname(sys.argv[0])
        default_playlist = os.path.join(exec_dir, DEFAULT_CHANNELS_FILENAME)
        if os.path.exists(default_playlist):
            print 'copying', default_playlist, 'to', my_playlist
            shutil.copy(default_playlist, my_playlist)

#def getFirstUrl(playlist):
#    lines = urllib.urlopen(str(playlist)).readlines()
#    for line in lines:
#        if not line.startswith('#'):
#            return line

def fix_webbrowser():
    'enable chromium browser support'
    for browser in ("xdg-open", "chromium-browser", "chrome", "chromium","chrome-browser"):
        if webbrowser._iscommand(browser):
            webbrowser.register(browser, None, webbrowser.BackgroundBrowser(browser))

def open_webbrowser(url):
    'try to use xfce preferred browser by default'
    try:
        webbrowser.get('xdg-open').open_new_tab(url)
    except:
        webbrowser.open_new_tab(url)

def config_win32_screensaver(toState):
    'http://miro.sourcearchive.com/documentation/2.5.4/windows-xul_2plat_2screensaver_8py-source.html'
    ''' Disable/resume screensaver on win32
    http://miro.sourcearchive.com/documentation/2.5.4/windows-xul_2plat_2screensaver_8py-source.html'''
    #needs ctypes on win32
    SPI_SETSCREENSAVEACTIVE = 17
    ctypes.windll.user32.SystemParametersInfoA(SPI_SETSCREENSAVEACTIVE, toState, None, 0)

def suspend_screensaver():
    'suspend linux screensaver'
    if is_win32 and ctypes:
        config_win32_screensaver(0)
        return
    # get root windows id
    
    ## alternative method from http://www.lucidelectricdreams.com/2011/06/disabling-screensaverlock-screen-on.html
    ## better? method: http://program-nix.blogspot.com/2010/08/python-inhibiting-gnome-screensaver.html
    print "subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled false', shell=True)"
    proc = subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled false', shell=True)
    print proc.wait()
#     print subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled false', stdout=subprocess.PIPE, shell=True).stdout.read().strip()
#     Inhibition command is:

    
    
    ######### OLD CODE
#     global window_id
#     os.sys
#     try:
#         window_id = subprocess.Popen('xwininfo -root | grep xwininfo | cut -d" " -f4', stdout=subprocess.PIPE, shell=True).stdout.read().strip()
#     except Exception, e:
#         print 'ERROR SUSPENDING SCREENSAVER', e
#     print 'xwininfo -root output', window_id
# 
#     #run xdg-screensaver on root window
#     xdg_output = subprocess.call(['xdg-screensaver', 'suspend', window_id])
#     print 'xdg-screen-suspend', window_id, 'ret:', xdg_output

def resume_screensaver():
    'resume linux screensaver'
    if is_win32 and ctypes:
        config_win32_screensaver(1)
        return
    print "Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled true', shell=True)"
    proc = subprocess.Popen('gsettings set org.gnome.desktop.screensaver idle-activation-enabled true', shell=True)
    print proc.wait()
    ### OLD CODE ###
#     if window_id:
#         xdg_output = subprocess.Popen('xdg-screensaver resume ' + window_id, stdout=subprocess.PIPE, shell=True).stdout.read().strip()
#         print 'xdg-screen-resume', window_id, xdg_output

def get_icon_resource(hex_icon_data):
    data = hex_icon_data.decode("base64")
    pixmap = QPixmap()
    pixmap.loadFromData(data, 'PNG')
    return QIcon(pixmap)

# def vlc_remote_command(command):
#     ''''this only works if you manually set in VLC the socket name
#     to ~/vlc.socket'''
#     if is_win32:
#         return False
#     print 'Running VLC Remote control command %s... ' % command
#     try:
#         s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
#         socket_file = os.path.join(os.path.expanduser("~"), VLC_SOCKET_FNAME)
#         print 'VLC Socket:', socket_file
#         s.connect(socket_file)
#         print s.send(command)
#         s.close()
#     except Exception as e:
#         show_gui_error(e)
#         return False
#     return True
#
#class CommandRunner():
#    def __init__(self, full_command, shell=False, stdin=None, stdout=None):
#        self.command = full_command
#        self.shell = shell
#        self.process_handle = None
#        self.stdout = stdout
#        self.stdin = stdin
#        
#    def run(self):
#        self.process_handle = subprocess.Popen(self.command.split(), stdout=self.stdout, shell=self.shell)
#        return self.process_handle

class MediaItem (QListWidgetItem):
    '''A list item can be eithera metadata line or a URL'''
    num_items = 0
    def __init__(self, item_text, icon):
        QListWidgetItem.__init__(self)
        self.params = {}
        self.is_meta = False
        self.is_totem = False
        if item_text.startswith('#'):
            'this is a metadata line, so set color and make it unselectable '
            self.is_meta = True
            if item_text.startswith('#EXTINF'):
                item_text = item_text[11:]
            else:
                item_text = item_text[1:]
            self.parse_params(item_text)
            self.setFlags(self.flags() ^ Qt.ItemIsSelectable ^ Qt.ItemIsEnabled)
            self.setBackgroundColor(QColor('#CEE1B6'))
            self.setTextColor(QColor('#9F5700'))
        else:
            'this is a URL'
            self.setIcon(icon)
            MediaItem.num_items += 1
        self.setText(item_text)
        self.setStatusTip(item_text)

    def parse_params(self, item_text):
        'read params like ;player=mplayer; ontop=yes into a dictionary object'
        params = item_text.split(';')
#        print 'DBG:', params
        if len(params):
            pairs = [param.strip().split('=', 1) for param in params if '=' in param]
            pairs = [(pair[0], pair[1]) for pair in pairs if len(pair) and (pair[0].lower() in VALID_META_KEYS)]

            if len(pairs):
                try:
                    self.params = dict(pairs)
                except Exception as e:
                    show_gui_error(e,'Error parsing params for item: %s' % item_text)

class MyForm(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        # setGeometry(x_pos, y_pos, width, height)
        self.setGeometry(100, 150, 500, 460)
        self.readSettings()
        self.setWindowTitle(APPNAME + ' ' + VERSION + " - " + self.chanells_path  )
        self.icon_path = get_icon_resource(imgdata_png_main)
#        self.icon_path = os.path.join(os.path.dirname(sys.argv[0]), 'chanchan.ico')
        self.icon = QIcon(self.icon_path)
        self.setWindowIcon(self.icon)
        self.chanells = None
        self.chanells_all = None
        self.num_channels = 0

        # the player subprocess process
        self.proc = None
        self.proc_sopcast = None
        self.is_sopcast = False
        self.is_playlist = False
        self.on_top = False
        self.cache_size = CACHE_SIZE_DEFAULT

        if not self.haveSeenFirstTime:
            copy_default_playlist()
            self.haveSeenFirstTime = True
            # saving settings should be done in closeEvent() instead!
#            settings = QSettings(QSettings.IniFormat, QSettings.UserScope, "xh", "chanchan")
#            settings.setValue("seen_first_time", QVariant(self.haveSeenFirstTime))
#            settings.sync()            

        # use a grid layout for the widgets
        grid = QGridLayout()

        # bind the button click to a function reference
        # new connect style, needs PyQt 4.5+
        ###btn_load.clicked.connect(self.load_channels_data)

        btn_play = QPushButton("&Play")
        btn_play.setToolTip("Click to play selected stream")

        btn_play.clicked.connect(self.on_button_play)
        btn_kill = QPushButton("&Stop")
        btn_kill.setToolTip("Click to stop current player")
        btn_kill.clicked.connect(self.kill_proc)

        self.listbox = QListWidget()
        # new connect style, needs PyQt 4.5+
        self.listbox.clicked.connect(self.on_select)

        self.listbox.doubleClicked.connect(self.on_double_click)

        # attach right-click handler
        self.listbox.setContextMenuPolicy(Qt.ActionsContextMenu)
        #self.listbox.setContextMenuPolicy(Qt.CustomContextMenu)
        #http://talk.maemo.org/showthread.php?t=64034
        self.actionCopyUrl = QAction("Copy URL", self.listbox)
        self.connect(self.actionCopyUrl, SIGNAL("triggered()"), self.copy_to_clipboard)
        self.actionPlay = QAction("Play", self.listbox)
        self.actionPlay.setShortcut("Ctrl+P")
        self.actionRestartPlayer = QAction("Restart SopPlayer", self.listbox)
        self.actionReloadChannels = QAction("Reload List", self.listbox)
        self.actionEditChannels = QAction("Edit Playlist", self.listbox)
        self.actionEditChannels.setShortcut("Ctrl+E")
        self.actionOpenChannelsFile = QAction("Open Playlist File", self.listbox)
        self.actionEditSource = QAction("Edit Source", self.listbox)
        self.actionAbout = QAction("About %s" % APPNAME, self.listbox)
        self.actionQuit = QAction("Quit", self)
        self.actionQuit.setShortcut("Ctrl+Q")
        self.search = QLineEdit()

        self.connect(self.search, SIGNAL("textChanged(QString)"), self.on_search_text_change)

        # clear button
        self.clear_button = QToolButton()
        self.clear_button.setIcon(get_icon_resource(imgdata_png_clear))
        self.clear_button.setIconSize(QSize(16, 16))
        self.clear_button.setCursor(Qt.ArrowCursor)
        self.clear_button.setAutoRaise(True)
        self.clear_button.setEnabled(False)
#        self.main_layout.addWidget(self.clear_button)
        self.connect(self.clear_button, SIGNAL("clicked()"), self.clear_search_text)

        self.listbox.addAction(self.actionPlay)
        self.listbox.addAction(self.actionRestartPlayer)
        self.listbox.addAction(self.actionCopyUrl)
        self.listbox.addAction(self.actionOpenChannelsFile)
        self.listbox.addAction(self.actionReloadChannels)
        self.listbox.addAction(self.actionEditChannels)
        self.listbox.addAction(self.actionEditSource)
        self.listbox.addAction(self.actionAbout)
        self.addAction(self.actionQuit)

        self.connect(self.actionPlay, SIGNAL("triggered()"), self.on_double_click)
        self.connect(self.actionRestartPlayer, SIGNAL("triggered()"), self.restart_sopplayer)
        self.connect(self.actionReloadChannels, SIGNAL("triggered()"), lambda: self.load_channels_data(self.chanells_path))
        self.connect(self.actionEditChannels, SIGNAL("triggered()"), lambda: self.edit_file(str(self.chanells_path)))
        self.connect(self.actionOpenChannelsFile, SIGNAL("triggered()"), lambda: self.load_channels_data())
        self.connect(self.actionEditSource, SIGNAL("triggered()"), lambda: self.edit_file(path=sys.argv[0], editor=EDITOR))
        self.connect(self.actionQuit, SIGNAL("triggered()"), self.close)
        self.connect(self.actionAbout, SIGNAL("triggered()"), lambda: QMessageBox.about(self, 'About %s' % APPNAME,
'''


<h4>%s version %s</h4>
<p>
Created by <i>%s</i></p>

<p><a href="mailto:%s">%s</a></p>
<p><a href="%s">chanchantv.googlecode.com</a></p>
''' % (APPNAME, VERSION, AUTHOR, EMAIL.decode('base64'), EMAIL, WEB)) #  warning(self, APPNAME, 'No playlist selected')
)


#        self.listbox.connect(self.listbox, SIGNAL("customContextMenuRequested(QPoint)"),
#                             self.on_right_click)

#        self.txtChanInfo = QLineEdit()
#        self.txtChanInfo.setReadOnly(True)

#        self.logWindow = QTextEdit()
#        self.logWindow.setSizePolicyx(QSizePolicy.)
        self.status = QLabel()
        self.status.setText('channels')
        # ADD BEVELED BORDER::self.status.setFrameStyle(QFrame.Panel | QFrame.Sunken)

        self.groupBox = QGroupBox("Engine")
        sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.groupBox.sizePolicy().hasHeightForWidth())
        self.groupBox.setSizePolicy(sizePolicy)
#        self.groupBox.setAutoFillBackground(True)

        self.rbMplayer = QRadioButton('&Mplayer', self.groupBox)
        self.rbMplayer.setChecked(True)
        self.rbMplayer.setToolTip("Play with Mplayer")
        #self.rbGstreamer = QRadioButton('gst&123', self.groupBox)
        self.rbVlc = QRadioButton('&Vlc', self.groupBox)
        self.rbVlc.setToolTip("Play with VLC")
        self.rbTotem = QRadioButton('&Totem', self.groupBox)
        self.rbTotem.setToolTip("Play with Totem")
        self.rbBrowser = QRadioButton('&Browser', self.groupBox)
        self.rbBrowser.setToolTip("Open URL in web browser")
        self.hBoxTop = QHBoxLayout()
        self.hBoxTop.addWidget(self.rbMplayer)
        #self.hBoxTop.addWidget(self.rbGstreamer)
        self.hBoxTop.addWidget(self.rbVlc)
        self.hBoxTop.addWidget(self.rbTotem)
        self.hBoxTop.addWidget(self.rbBrowser)
        self.groupBox.setLayout(self.hBoxTop)

        self.cbPlaylistFlag = QCheckBox('Playlist')
        self.cbPlaylistFlag.setToolTip('Resource is a M3U, ASX or PLS playlist')

        self.cbFullScreen = QCheckBox('Full Screen')
        self.cbFullScreen.setToolTip('Start video in full screen')
        self.cbFullScreen.setChecked(self.is_full_screen)
        self.cbInhibitScreensaver = QCheckBox('Inhibit Screensaver')
        self.cbInhibitScreensaver.setToolTip('Disable screensaver while playing stream')
        self.cbInhibitScreensaver.setChecked(self.is_inhibit_screen)
#        addWidget(widget, row, column, rowSpan, columnSpan)
        grid.addWidget(self.groupBox, 0, 0, 1, 3)
        grid.addWidget(btn_play, 0, 4, 1, 1)
        grid.addWidget(btn_kill, 0, 5, 1, 1)
        grid.addWidget(self.search, 1, 0, 1, 4)
        grid.addWidget(self.clear_button, 1, 3, 1, 1)
        grid.addWidget(self.status, 1, 5, 1, 1)
        # listbox spans over 5 rows and 2 columns
        grid.addWidget(self.listbox, 2, 0, 5, 6)
        ## BAD grid.addWidget(self.hBoxFlags, 6, 0, 1, 1)
        grid.addWidget(self.cbPlaylistFlag, 7, 0, 1, 1)
        grid.addWidget(self.cbFullScreen, 7, 1, 1, 1)
        grid.addWidget(self.cbInhibitScreensaver, 7, 2, 1, 1)
#        grid.addWidget(self.txtChanInfo, 7, 0, 1, 6)
#        grid.addWidget(self.logWindow, 8, 0, 1, 6)
        self.setLayout(grid)
        self.search.setFocus()
        self.load_channels_data(self.chanells_path)

    def clear_search_text(self):
        print '------clear-search-text---------'
        self.search.clear()

    def on_search_text_change(self):
        if not self.chanells_all:  # only need to do this once
            self.chanells_all = list(self.chanells)
        text = str(self.search.text()).strip()

        print 'DBG', len(text), len(self.chanells_all)

        if len(text) > 1:
            self.clear_button.setEnabled(True)
            filtered_list = self.get_matching_items(text.lower(), self.chanells_all)
            if len(filtered_list):
                self.chanells = filtered_list
            else:
                self.chanells = []
        else:
            self.chanells = list(self.chanells_all)
            self.clear_button.setEnabled(False)
        self.load_channels_data(None, False)

    def get_matching_items(self, needle, haystack):
        'search for a substring in channel list'
        matches = []
        found_in_meta = False
        last_meta_item = None

        for ch in haystack:
            is_meta = ch.startswith('#')
            if is_meta and not needle in ch.lower():
                last_meta_item = ch
            if needle in ch.lower():
                if is_meta:
                    found_in_meta = True
                elif not found_in_meta and last_meta_item not in matches:
                    matches.append(last_meta_item)
                matches.append(ch)
            elif found_in_meta:
                if not is_meta:
                    matches.append(ch)
                else:
                    found_in_meta = False
        return matches

    def closeEvent(self, event):
        self.writeSettings()
        print 'closeEvent: Saving settings and exiting...'
        return
        quit_msg = "Are you sure you want to exit the program?"
        reply = QMessageBox.question(self, 'Message',
                                           quit_msg, QMessageBox.Yes, QMessageBox.No)

        if reply == QMessageBox.Yes:
            self.writeSettings()
            event.accept()
            QApplication.instance().quit()
        else:
            event.ignore()

    def keyPressEvent(self, event):
        if event.key() == Qt.Key_Escape:
            self.search.setFocus()
            self.search.selectAll()

    def copy_to_clipboard(self):
        clipboard = QApplication.clipboard()
        clipboard.setText(self.listbox.currentItem().text())

    def load_channels_data(self, new_path=None, read_from_file=True):
        MediaItem.num_items = 0
        if read_from_file:
            if not new_path:
                new_path = str(self.get_new_filename())
                if not new_path:
    #                QMessageBox.warning(self, APPNAME, 'No playlist selected')
                    return

            try:
                fh = codecs.open(new_path, 'r', 'utf8')
                self.chanells = [ch.strip() for ch in fh.readlines()]
            except Exception as e:
                show_gui_error(e, 'File not found', 'Error opening playlist "%s" \n\n%s' % (new_path, str(e)))
                return

            self.chanells_path = new_path
            self.chanells = [ch.strip() for ch in self.chanells]
            self.chanells_all = None
        self.listbox.clear()
        current_params = None

        for chan in self.chanells:
            if not len(chan) or chan.strip() == '#EXTM3U':
                continue
            item = MediaItem(chan, self.icon)
            if item.is_meta:
                '''if a metadata line, then store and apply to all the following
                non-metadata items
                '''
                current_params = item.params
            elif current_params:
                item.params = current_params

            item.setStatusTip(chan)
            self.listbox.addItem(item)
        self.setWindowTitle(APPNAME + ' ' + VERSION + ' - ' + self.chanells_path)
        self.status.setText(str(MediaItem.num_items) + ' channels')

    def edit_file(self, path, editor=EDITOR):
        if ' ' in editor:
            editor = editor.split(' ')
        subprocess.Popen([editor, path])

    def get_new_filename(self):
        return QFileDialog.getOpenFileName(self,
                       'Load Playlist file',
                       '',
                       "Playlist files (*.m3u);;All Files (*.*);")

    def on_button_play(self):
#        self.on_select()
        self.play_media()

    def on_select(self):
        """an item in the listbox has been clicked/selected"""
        current_item = self.listbox.currentItem()
        if not current_item:
            return
        current_channel = current_item and str(current_item.text()) or '<no channel>'
        self.is_playlist = current_channel[-4:].lower() in ['.m3u', '.asx', '.pls']

#        if current_channel.startswith('sop:'):
            #self.rbMplayer.setChecked(True)
#            self.cache_size = '1024'

        if current_item.params:
            'set params for current channel according to metadata line'
            myparams = current_item.params.keys()

            if 'player' in myparams:
                player = current_item.params['player'].lower()
                if player == 'totem':
                    self.rbTotem.setChecked(True)
                elif player == MPLAYER:
                    self.rbMplayer.setChecked(True)
                #elif player == 'gst123':
                #    self.rbGstreamer.setChecked(True)
                elif player == VLC:
                    self.rbVlc.setChecked(True)
                elif player in ('browser', 'web'):
                    self.rbBrowser.setChecked(True)

            if 'playlist' in myparams or 'pl' in myparams:
                self.is_playlist = current_item.params['playlist'].lower() in GOOD_VALUES

            if 'fullscreen' in myparams or 'fs' in myparams:
                self.is_full_screen = current_item.params['fullscreen'].lower() in GOOD_VALUES
            else:
                self.is_full_screen = IS_FULLSCREEN_DEFAULT

            if 'ontop' in myparams or 'top' in myparams:
                self.on_top = current_item.params['top'].lower() in GOOD_VALUES
            else:
                self.on_top = IS_ON_TOP_DEFAULT

            if 'cache' in myparams:
                self.cache_size = current_item.params['cache']
            else:
                self.cache_size = CACHE_SIZE_DEFAULT
                
            if 'exec' in myparams or 'shell' in myparams:
                # set shell options: console or no console
                self.exec_shell_command = current_item.params['exec']
#
#            if 'exec' in myparams:
#                self.executable_name = current_item.params['exec']


            self.cbPlaylistFlag.setChecked(self.is_playlist)
            
            # only setting True state
            if self.is_full_screen:
                self.cbFullScreen.setChecked(True)

    def on_double_click(self):
        self.play_media()
        """an item in the listbox has been double-clicked"""

    def restart_sopplayer(self):
        # if self.rbVlc.isChecked():
            # if vlc_remote_command('testing if vlc remote is running...'):
            #     vlc_remote_command('add %s' % SOPCAST_SERVER_URL)
            #     vlc_remote_command('volume 200')
        # else:
        self.play_media(start_sopcast_server=False)

    def play_media(self, start_sopcast_server=True):
        current_item = self.listbox.currentItem()
        if not current_item:
            return
        current_channel = str(current_item.text())

        if self.proc and self.proc.pid:
            self.kill_proc()

        args = []

        if self.cbInhibitScreensaver.isChecked():
            suspend_screensaver()

        ################ RUN SHELL COMMAND #############
        if 'exec' in current_item.params:
            show_console = current_item.params['exec'].lower() == 'console'
            if show_console:
                args += ['xterm', '-geometry', '45x8-20+400', '-e']
                args += [current_channel.strip()]
                self.proc = subprocess.Popen(args, stdout=subprocess.PIPE)
                print 'DBG:', self.proc
            else:
                args.insert(0, current_item.params['exec'])
                args += [current_channel.strip()]
                self.proc = subprocess.Popen(args, shell=True)
            return

        # don't use xterm for vlc, totem
        if (self.rbMplayer.isChecked()):
            if not is_win32 and not is_osx:
                args += ['xterm', '-geometry', '45x8-20+150', '-e']

        self.is_sopcast = current_channel.lower().startswith('sop://')

        if self.is_sopcast:
            if start_sopcast_server:
                # args_sopcast = ['xterm', '-geometry', '45x8-20+400', '-e', sopcast_binary, current_channel, SOPCAST_LISTEN_PORT, SOPCAST_SERVER_PORT]
                try:
                    print 'Waiting for sopcast server starup at %s ...' % current_channel
                    self.proc_sopcast = run_command_in_new_terminal(
                        sopcast_binary,
                        current_channel,
                        SOPCAST_LISTEN_PORT,
                        SOPCAST_SERVER_PORT)
                        #
                except Exception as e:
                    show_gui_error(e, """ERROR! Sopcast executable not found or other error:

To install sopcast support on Linux, run:

%s""" % (SOPCAST_INSTALL_HOWTO))
                    return

            current_channel = SOPCAST_SERVER_URL
            time.sleep(SOPCAST_SERVER_WAIT_SECS)

        if self.rbMplayer.isChecked():

            if is_win32:
                args = ['cmd', '/c', MPLAYER_PATH_WIN32, '-cache-min', CACHE_SIZE_MIN, '-cache', self.cache_size]
            else:
                args += [MPLAYER, '-cache-min', CACHE_SIZE_MIN, '-cache', self.cache_size]

            self.on_top and args.append('-ontop')
            self.cbFullScreen.isChecked() and args.append('-fs')
            self.cbPlaylistFlag.isChecked() and args.append('-playlist')

        #elif self.rbGstreamer.isChecked():
        #    args.append('gst123')
        #    if '.m3u' in current_channel:
        #        current_channel = getFirstUrl(current_channel)

        elif self.rbVlc.isChecked():
            if is_win32:
                if os.path.exists(VLC_PATH_WIN32):
                    args = [VLC_PATH_WIN32]
                elif os.path.exists(VLC_PATH_WIN32_CUSTOM):
                    args = [VLC_PATH_WIN32_CUSTOM]
            elif is_osx:
                args = [VLC_PATH_OSX]#, '--one-instance']
            else:
                args = ['vlc']#, '--one-instance']
            # if vlc_remote_command('testing if vlc remote is running...'):
            #     # print 'VLC Remote Control not running, starting new VLC instance...'
            #     vlc_remote_command('add %s' % current_channel)
            #     vlc_remote_command('volume 150')
            #     return
            # else:
            #     print 'VLC Remote Control not running, starting new VLC instance...'
            self.cbFullScreen.isChecked() and args.append('--fullscreen')
            args.append('--video-on-top')

        elif self.rbTotem.isChecked():
            args += ['totem', '--replace']
            # FIXME!!! totem segfaults when started with the --fullscreen switch
#            self.cbFullScreen.isChecked() and args.append('--fullscreen')

        elif self.rbBrowser.isChecked():
            open_webbrowser(current_channel)
            return

        args.append(current_channel)

        print args

        try:
            if is_win32:
                #self.proc = subprocess.Popen(args, creationflags=subprocess.STARTF_USESHOWWINDOW, cwd=os.path.dirname(sys.argv[0]))

## TODO: get right options on win32
#http://stackoverflow.com/questions/7006238/how-do-i-hide-the-console-when-i-use-os-system-or-subprocess-call
#startupinfo = subprocess.STARTUPINFO()
#startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
#subprocess.call('taskkill /F /IM exename.exe', startupinfo=startupinfo)

                self.proc = subprocess.Popen(args, shell=True, cwd=os.path.dirname(sys.argv[0]))
            else:
                self.proc = subprocess.Popen(args, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
                print 'DBG:', type(self.proc), self.proc
#                console_data = self.proc.stdout.read()
#                self.logWindow.setText(console_data)


        except Exception as e:
            show_gui_error(e, "ERROR! Selected player not available:\n")

    def kill_proc(self):
        if self.cbInhibitScreensaver.isChecked():
            resume_screensaver()
        if self.proc and not self.rbVlc.isChecked():
            try:
                self.proc.kill()
            except:
                pass
        if self.is_sopcast and self.proc_sopcast:
            try:
                self.proc_sopcast.kill()
                os.system('killall sopcast')
            except:
                pass

    def readSettings(self):
        # store settings object
        self.settings = QSettings(QSettings.IniFormat, QSettings.UserScope, "xh", "chanchan")
        pos = self.settings.value("pos", QVariant(QPoint(200, 200))).toPoint()
        size = self.settings.value("size", QVariant(QSize(400, 400))).toSize()
        self.resize(size)
        self.move(pos)
        self.chanells_path = self.settings.contains('channels_file') and str(self.settings.value("channels_file").toString()) or get_default_channels_path()
        self.is_inhibit_screen = self.settings.contains('inhibit_screen') and self.settings.value("inhibit_screen").toBool()
        self.is_full_screen = self.settings.contains('fullscreen') and self.settings.value("fullscreen").toBool()
        self.haveSeenFirstTime = self.settings.contains('seen_first_time') and self.settings.value("seen_first_time").toBool()

    def writeSettings(self):
        settings = QSettings(QSettings.IniFormat, QSettings.UserScope, "xh", "chanchan")
        settings.setValue("pos", QVariant(self.pos()))
        settings.setValue("size", QVariant(self.size()))
        settings.setValue("channels_file", QVariant(self.chanells_path))
        settings.setValue("inhibit_screen", QVariant(self.cbInhibitScreensaver.isChecked()))
        settings.setValue("fullscreen", QVariant(self.cbFullScreen.isChecked()))
        settings.setValue("seen_first_time", QVariant(self.haveSeenFirstTime))
        settings.sync()
#       self.setWindowTitle(current_channel)

app = QApplication([])
form = MyForm()
fix_webbrowser()
form.show()
app.exec_()
